let Const = require('./const');
let Log = require('./log');
let Util = require('./util');
let Config = require('./config');

let Core = {
	ACTION: null,

	initContext: (req, res, next) => {
		let Context = {
			request: req,
			response: res,
			params: Util.merge(req.query, req.params, req.body),
			next: next,
			user: undefined,

			update: function () {
				Context.params = Util.merge(Context.request.query, Context.request.params, Context.request.body);
				return Context;
			},

			errorFinish: function (code, message) {
				Context.response.json({
					code: code,
					message: message
				}).end();
			},

			finish: function (data) {
				let object = {
					code: 200,
					content: null,
					message: null,
					success: true
				};

				if (data) {
					object.content = data;
				}

				Context.response.json(object).end();
			},

			processError: function (error) {
				if (error.hasOwnProperty('code') && error.hasOwnProperty('message')) {
					return Context.errorFinish(error.code, error.message);
				}

				Context.errorFinish(Const.ERROR.ERROR_NOT_FOUND, '' + error);
			}
		};

		return Context;
	},

	install: (router, actionMap) => {
		let ACTION = {};

		for (let route in actionMap) {
			if (actionMap.hasOwnProperty(route)) {
				let action = actionMap[route];
				let func = action[0];
				let arg = action.slice(1);

				let argList = [];
				for (let i = 0; i < arg.length; i++) {
					argList.push(Util.parseApiKey(arg[i]));
				}

				ACTION[route] = {
					func: func,
					argList: argList
				};

				router.get(route, Core.runAction);
				router.post(route, Core.runAction);
			}
		}
		Core.ACTION = ACTION;
	},

	hasAction: (route) => {
		return Core.ACTION.hasOwnProperty(route);
	},

	getAction: (route) => {
		return Core.ACTION[route];
	},

	runAction: (req, res, next) => {
		let path = req.route.path;
		path = Util.trimEnd(path, '/');

		Log.d("********* request data start *********");
		Log.d(Util.getFormattedJson({
			path: path,
			params: Util.merge(req.query, req.params, req.body)
		}));
		Log.d("********* request data end *********");

		if (Core.hasAction(path)) {
			let context = Core.initContext(req, res, next);
			context = context.update();
			let {func, argList} = Core.getAction(path);
			let params = [context];

			for (let i = 0; i < argList.length; i++) {
				let {key, defaultValue} = argList[i];
				let value = undefined;
				if ((context.params.hasOwnProperty(key) && !context.params[key]) || !context.params.hasOwnProperty(key)) {
					if (defaultValue === undefined) {
						return context.errorFinish(Const.ERROR.ERROR_PARAM_NOT_SET, `param ${key} not set`);
					}

					value = defaultValue;
				}
				else {
					value = context.params[key];
				}

				params.push(value);
			}
			func.apply(null, params);
		}
		else {
			next();
		}
	},

	errorHandler: (err, req, res, next) => {
		console.log(err);
		res.status(500).json({
			code: Const.ERROR.ERROR_INTERNAL_SERVER,
			message: 'an internal error occurred'
		}).end();
	},

	notFoundHandler: (req, res) => {
		res.status(404).json({
			code: Const.ERROR.ERROR_NOT_FOUND,
			message: 'not found'
		}).end();
	}
};

Core.Const = Const;
Core.Log = Log;
Core.Util = Util;
Core.Config = Config;

module.exports = Core;
